using System;
using System.Collections.Generic;
using System.Linq;
using MathSharp;

namespace MathSharp.Script
{
	public class Namespace
	{
		public List<Namespace> Parents {
			get;
			protected set;
		}
		
		public string Name {
			get;
			protected set;
		}
		
		public Script Script {
			get;
			protected set;
		}
		
		public List<Variable> Varibles {
			get;
			protected set;
		}
		
		public List<DeclareFunctionCommand> Functions {
			get;
			protected set;
		}
		
		public List<DeclareOperatorCommand> Operations {
			get;
			protected set;
		}
		
		public Namespace (Script script, string name, IEnumerable<Namespace> parents)
		{
			this.Script = script;
			this.Name = name;
			if (parents == null)
				Parents = new List<Namespace>();
			else
				Parents = parents.ToList();
			
			Varibles = new List<Variable>();
			Functions = new List<DeclareFunctionCommand>();
			Operations = new List<DeclareOperatorCommand>();
		}
		
		public Variable TryGetVarible(string name) {
			Variable res = Varibles.FirstOrDefault(v => v.Name == name);
			if (res != null)
				return res;
			foreach (Namespace ns in Parents) {
				res = ns.TryGetVarible(name);
				if (res != null)
					return res;
			}
			return null;
		}

		public DeclareFunctionCommand TryGetFunction(string name) {
			DeclareFunctionCommand res = Functions.FirstOrDefault(f => f.Name == name);
			if (res != null)
				return res;
			foreach (Namespace ns in Parents) {
				res = ns.TryGetFunction(name);
				if (res != null)
					return res;
			}
			return null;
		}
		
		public DeclareOperatorCommand TryGetOperation(string operatorName, Value op1, Value op2, Value op3) {
			int opCount = (op3 != null)?3:(op2 != null)?2:(op1 != null)?1:0;
			if (opCount <= 0)
				throw new ArgumentNullException("op1", "Хотябы один из операндов должен быть отличен от нуля");
			DeclareOperatorCommand res = null;
			foreach (DeclareOperatorCommand o in Operations.FirstOrDefault(o => o.Name == name &&
			                                                                    o.Arguments.Count == opCount)) {
				if (op1.Type != o.Arguments[0].Type)
					continue;
				if (opCount >= 2 && op2.Type != o.Arguments[1].Type)
					continue;
				if (opCount >= 3 && op3.Type != o.Arguments[2].Type)
					continue;
				res = o;
				break;
			}
			if (res != null)
				return res;
			// если вариант не найден, то ищем в немспейсах выше
			foreach (Namespace ns in Parents) {
				res = ns.TryGetVarible(name);
				if (res != null)
					return res;
			}
			return null;
		}
	}
}

